package com.paranoidmonkey.ode;

import java.util.ArrayList;
import java.util.List;

import android.util.Log;

import com.paranoidmonkey.ode.Joint.AMotor;
import com.paranoidmonkey.ode.Joint.Ball;
import com.paranoidmonkey.ode.Joint.Fixed;
import com.paranoidmonkey.ode.Joint.Hinge;
import com.paranoidmonkey.ode.Joint.Hinge2;
import com.paranoidmonkey.ode.Joint.Joint;
import com.paranoidmonkey.ode.Joint.JointGroup;
import com.paranoidmonkey.ode.Joint.LMotor;
import com.paranoidmonkey.ode.Joint.Null;
import com.paranoidmonkey.ode.Joint.PR;
import com.paranoidmonkey.ode.Joint.PU;
import com.paranoidmonkey.ode.Joint.Piston;
import com.paranoidmonkey.ode.Joint.Plane2D;
import com.paranoidmonkey.ode.Joint.Slider;
import com.paranoidmonkey.ode.Joint.Universal;

public class Simulation {

	private World world;
	private Space space;

	private List<Geom> geoms;
	private List<Body> bodies;
	private List<Joint> joints;

	public enum geomType{
		BOX,
		CAPSULE,
		CYLINDER,
		TRANSFORM,
		PLANE,
		SPHERE
	};
	public enum jointType{
		Ball,
		Hinge,
		Slider,
		Universal,
		Hinge2,
		Fixed,
		Null,
		AMotor,
		LMotor,
		Plane2D,
		PR,
		PU,
		Piston
	};

	private Simulation(Space s){
		geoms=new ArrayList<Geom>();
		bodies=new ArrayList<Body>();
		joints=new ArrayList<Joint>();

		world=new World();
		space=s;
	}

	public static Simulation CreateSimple(){
		return new Simulation(Space.createSimpleSpace(null));
	}
	public static Simulation CreateQuadTree(Vector3 Center,Vector3 Extents,int Depth){
		return new Simulation(Space.createQuadTreeSpace(null, Center, Extents, Depth));
	}
	public static Simulation CreateHash(){
		return new Simulation(Space.createHashSpace(null));
	}
	public static Simulation CreateSweepAndPrune(int axisorder){
		return new Simulation(Space.createSweepAndPruneSpace(null, axisorder));
	}

	public World getWorld(){
		return world;
	}
	public Space getSpace(){
		return space;
	}
	public List<Geom> getGeoms(){
		return geoms;
	}
	public List<Body> getBodies(){
		return bodies;
	}
	public List<Joint> getJoints(){
		return joints;
	}

	public synchronized Geom createGeom(geomType type,float[] vars,Vector3 initialPosition,Quaternion initialRotation){
		simpaused=true;
		while (simrunning);

		Geom g;

		Log.d("Simulation","Creating Geom");
		switch(type){
		case BOX: g=new Box(space,vars[0],vars[1],vars[2]);
		break;
		case CAPSULE: g=new Capsule(space,vars[0],vars[1]);
		break;
		case CYLINDER: g=new Cylinder(space,vars[0],vars[1]);
		break;
		case TRANSFORM: g=new GeomTransform(space);
		break;
		case PLANE: g=new Plane(space,vars[0],vars[1],vars[2],vars[3]);
		break;
		case SPHERE: g=new Sphere(space,vars[0]);
		break;
		default:
			return null;
		}

		if (g.isNull()){
			Log.d("Simulation","Geom not created");	
			return null;
		}

		Log.d("Simulation","Geom created @"+g.getHandle());	

		Log.d("Simulation","Setting data");
		g.setData(null);

		Log.d("Simulation","Setting initial position");
		g.setPosition(initialPosition.x(), initialPosition.y(), initialPosition.z());
		g.setQuaternion(initialRotation);

		geoms.add(g);
		simpaused=false;
		return g;
	}

	public synchronized TriMesh createTriMesh(TriMeshData d,Vector3 initialPosition,Quaternion initialRotation){
		simpaused=true;
		while (simrunning);

		Log.d("Simulation","Creating TriMesh");
		TriMesh g=new TriMesh(space, d);
		
		if (g.isNull()){
			Log.d("Simulation","TriMesh not created");	
			return null;
		}

		Log.d("Simulation","Setting data");
		g.setData(null);

		Log.d("Simulation","Setting initial position");
		g.setPosition(initialPosition.x(), initialPosition.y(), initialPosition.z());
		g.setQuaternion(initialRotation);

		geoms.add(g);
		simpaused=false;
		return g;
	}

	public synchronized TriMesh createTriMesh(TriMeshData d,Body b,Vector3 offsetPosition,Quaternion offsetRotation){
		simpaused=true;
		while (simrunning);

		Log.d("Simulation","Creating TriMesh");
		TriMesh g=new TriMesh(space, d);
		
		if (g.isNull()){
			Log.d("Simulation","TriMesh not created");	
			return null;
		}

		Log.d("Simulation","Setting data");
		g.setData(null);

		g.setBody(b);
		
		Log.d("Simulation","Setting initial position");
		g.setOffsetPosition(offsetPosition.x(), offsetPosition.y(), offsetPosition.z());
		g.setOffsetQuaternion(offsetRotation);

		geoms.add(g);
		simpaused=false;
		return g;
	}

	public synchronized Heightfield createHeightfield(HeightfieldData d,boolean placeable,Vector3 initialPosition,Quaternion initialRotation){
		simpaused=true;
		while (simrunning);

		Log.d("Simulation","Creating Heightfield");
		Heightfield g=new Heightfield(space, d, placeable);

		if (g.isNull()){
			Log.d("Simulation","Heightfield not created");	
			return null;
		}
		
		Log.d("Simulation","Heightfield created @"+g.getHandle());	

		Log.d("Simulation","Setting data");
		g.setData(null);

		if (placeable){
			Log.d("Simulation","Setting initial position");
			g.setPosition(initialPosition.x(), initialPosition.y(), initialPosition.z());
			g.setQuaternion(initialRotation);
		}
		
		geoms.add(g);
		simpaused=false;
		return g;		
	}

	public synchronized Body createBody(Vector3 initialPosition,Quaternion initialRotation){
		simpaused=true;
		while (simrunning);

		Log.d("Simulation","Creating Body");
		Body b=new Body(world);
		
		if (b.isNull()){
			Log.d("Simulation","Body not created");	
			return null;
		}

		b.setData(null);

		b.setPosition(initialPosition.x(), initialPosition.y(), initialPosition.z());
		b.setQuaternion(initialRotation);
		
		bodies.add(b);
		simpaused=false;
		return b;
	}

	public synchronized Joint createJoint(jointType type,JointGroup g,Body b1,Body b2){
		Joint j;

		simpaused=true;
		while (simrunning);
		
		switch(type){
		case Ball: j=new Ball(world,g);
			break;
		case Hinge: j=new Hinge(world, g);
			break;
		case Hinge2: j=new Hinge2(world,g);
			break;
		case Fixed: j=new Fixed(world,g);
			break;
		case Piston: j=new Piston(world,g);
			break;
		case Plane2D: j=new Plane2D(world,g);
			break;
		case PR: j=new PR(world,g);
			break;
		case PU: j=new PU(world,g);
			break;
		case Universal: j=new Universal(world,g);
			break;
		case AMotor: j=new AMotor(world,g);
			break;
		case LMotor: j=new LMotor(world,g);
			break;
		case Slider: j=new Slider(world,g);
			break;
		case Null: j=new Null(world,g);
			break;
		default:
			return null;
		}

		if (j.isNull()){
			Log.d("Simulation","Joint not created");	
			return null;
		}

		j.attach(b1, b2);
		
		joints.add(j);
		simpaused=false;
		return j;
	}

	public synchronized void destroyGeom(Geom g){
		simpaused=true;
		while (simrunning);

		if (geoms.remove(g)){
			g.destroy();
		}
		simpaused=false;
	}
	public synchronized void destroyBody(Body b){
		simpaused=true;
		while (simrunning);

		if (bodies.remove(b)){
			b.destroy();
		}
		simpaused=false;
	}
	
	public synchronized void addCollisionListener(ODECollisionListener l){
		Space.addCollisionListener(l);
	}
	public synchronized void removeCollisionListener(ODECollisionListener l){
		Space.removeCollisionListener(l);
	}
		
	int nextUpdateMS = 20; // millisec
	float stepSize = nextUpdateMS/1000.0f; // Step size, fixed
	
	boolean simrunning=false;
	boolean simpaused=false;
	
	public synchronized void tick(){
	
		if(simpaused==false){
			simrunning=true;
			space.collide();
			world.step(stepSize);
			Body.processCallbacks();
			simrunning=false;
		}
	}
	
	public synchronized void clear(){
		simpaused=true;
		while (simrunning);

		for (Geom g:geoms){
			g.destroy();
		}
		geoms.clear();

		for (Body b:bodies){
			b.destroy();
		}
		bodies.clear();

		for (Joint j:joints){
			j.destroy();
		}
		joints.clear();
		
		space.clean();
	}
}
